/* drv_lpit_edma.c */
#include "drv_lpit_edma.h"

/* EDMA传输通道配置模板 */
#define DRV_LPIT_EDMA_CONFIG_DEFAULT_STRUCT {              \
    .SrcAddr                     = 0U,                      \
    .DestAddr                    = 0U,                      \
    .MinorLoopByteCount          = 0U,                      \
    .SrcBusWidthMode             = eEDMA_BusWidth_1Byte,    \
    .DestBusWidthMode            = eEDMA_BusWidth_1Byte,    \
    .SrcAddrCycleMode            = eEDMA_AddrCycle_Disabled,\
    .DestAddrCycleMode           = eEDMA_AddrCycle_Disabled,\
    .SrcAddrIncPerTransfer       = 0,                       \
    .DestAddrIncPerTransfer      = 0,                       \
    .SrcAddrMinorLoopDoneOffset  = 0,                       \
    .DestAddrMinorLoopDoneOffset = 0,                       \
    .MinorLoopCount              = 0,                       \
    .SrcAddrMajorLoopDoneOffset  = 0,                       \
    .DestAddrMajorLoopDoneOffset = 0,                       \
}

void LPIT_EDMA_InitTxHw(
    LPIT_EDMA_TxHandler_T *Handler,         /*!< 创建传输句柄 */
    LPIT_EDMA_TxHwDev_T   *HwDevPtr,        /*!< EDMA及相关配置 */
    LPIT_ChannelConfig_T  *LPITConfigPtr    /*!< LPIT硬件配置 */
)
{
    /* 关联数据结构 */
    txHandler->TxHwDevPtr = txHwDevPtr;

    /* 如果需要的话，初始化LPIT */
    if (HwDevPtr->enResetLpit) 
    {
        LPIT_HAL_Reset(HwDevPtr->LPITBase);
    }
    LPIT_HAL_SetBaudrate(HwDevPtr->LPITBase, LPITConfigPtr->BusClkHz, LPITConfigPtr->Baudrate);

    /* 即使此处打开了触发开关并保持触发电平，只要DMAMUX不开闸，触发信号就进不了EDMA */
    DMAMUX_HAL_SetTriggerForDMA(txHwDevPtr->EdmaLPITTxChnIdx, eDMAMUX_TriggerSource_Disabled);
    EDMA_HAL_ResetChannel(txHwDevPtr->EdmaLPITTxChnIdx);

    LPIT_HAL_EnableTx(txHwDevPtr->LPITBase, true); /* 启用LPIT的发送引擎 */
    LPIT_HAL_EnableDmaOnTxEmpty(txHwDevPtr->LPITBase, true); /* 在LPIT端启用发送缓冲区空触发EDMA */
}

void LPIT_EDMA_StartTx(
    LPIT_EDMA_TxHandler_T *txHandler,
    uint8_t *txBufPtr, uint32_t txLen,
    void (*txDoneCallback)(void)
)
{
    LPIT_EDMA_TxHwDev_T *txHwDevPtr = txHandler->TxHwDevPtr;
    EDMA_TransferConfig_T edmaTransferConfigStruct = DRV_LPIT_EDMA_CONFIG_DEFAULT_STRUCT;

    /* 保存回调函数到xferHandler，每次传输均更新回调函数 */
    txHandler->TxDoneCallbackFunc = txDoneCallback;

    /* 配置EDMA通道之前确保通道是关闭的 */
    DMAMUX_HAL_SetTriggerForDMA(txHwDevPtr->EdmaLPITTxChnIdx, eDMAMUX_TriggerSource_Disabled);
    EDMA_HAL_ResetChannel(txHwDevPtr->EdmaLPITTxChnIdx);

    edmaTransferConfigStruct.SrcAddr = (uint32_t)(txBufPtr);
    edmaTransferConfigStruct.SrcAddrIncPerTransfer = 1; /* 在发送队列内递增1字节 */
    edmaTransferConfigStruct.DestAddr = (uint32_t)(&(txHwDevPtr->LPITBase->DATA));
    edmaTransferConfigStruct.MinorLoopCount = txLen;
    edmaTransferConfigStruct.MinorLoopByteCount = 1U; /* 每次传输一个字节 */
    EDMA_HAL_ConfigTransfer(txHwDevPtr->EdmaLPITTxChnIdx, &edmaTransferConfigStruct);

    /* 启用传输完成中断 */
    NVIC_EnableIRQ(txHwDevPtr->EdmaTxChnIRQn); /* 在NVIC中开启监听发送搬运完成中断信号 */
    EDMA_HAL_EnableInterruptOnMajorLoopDone(txHwDevPtr->EdmaLPITTxChnIdx, true);

    /* 解除对DMA通道的锁定，启动发送过程 */
    EDMA_HAL_EnableHwTrigger(txHwDevPtr->EdmaLPITTxChnIdx, true); /* 启用硬件触发 */
    EDMA_HAL_SetHwTriggerAutoDisabledOnMajorLoopDone(txHwDevPtr->EdmaLPITTxChnIdx, true); /* 发送完成自动关闭硬件触发 */
    DMAMUX_HAL_SetTriggerForDMA(txHwDevPtr->EdmaLPITTxChnIdx, txHwDevPtr->EdmaLPITTxTriggerSource); /* 放开EDMA触发信号的大门 */
}

/*
* EDMA搬运发送数据完成后，在EDMA完成中断服务程序中调用收尾操作
*/
void LPIT_EDMA_TxIRQHandler(LPIT_EDMA_TxHandler_T *txHandler)
{
    LPIT_EDMA_TxHwDev_T *txHwDevPtr = txHandler->TxHwDevPtr;

    /* 停用发送完成中断 */
    EDMA_HAL_EnableInterruptOnMajorLoopDone(txHwDevPtr->EdmaLPITTxChnIdx, false);
    NVIC_DisableIRQ(txHwDevPtr->EdmaTxChnIRQn);

    /* DMA触发窗口在搬运完成后自动关闭，此处不需要手动关闭窗口，但需要手动清除完成标志位 */
    EDMA_HAL_ClearMajorLoopDoneFlag(txHwDevPtr->EdmaLPITTxChnIdx);
    EDMA_HAL_ClearInterruptFlagOfMajorLoopDone(txHwDevPtr->EdmaLPITTxChnIdx);
    DMAMUX_HAL_SetTriggerForDMA(txHwDevPtr->EdmaLPITTxChnIdx, eDMAMUX_TriggerSource_Disabled);

    if (txHandler->TxDoneCallbackFunc)
    {
        (*(txHandler->TxDoneCallbackFunc))();
        txHandler->TxDoneCallbackFunc = NULL; /* 执行完毕后清零，防止下次传输误入 */
    }
}

/* EOF. */
